<!-- Copyright 2012 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -->
<!DOCTYPE html>
<title>Unit Test of e2e.crypt</title>
<script src="../../../../javascript/closure/base.js"></script>
<script src="test_js_deps-runfiles.js"></script>
<script>
  goog.require('goog.testing.AsyncTestCase');
  goog.require('goog.testing.jsunit');
  goog.require('e2e');
  goog.require('e2e.error.InvalidArgumentsError');
  goog.require('goog.math.Long');
  goog.require('goog.testing.asserts');
</script>
<script>
      var asyncTest = goog.testing.AsyncTestCase.createAndInstall(document.title);
      /**
       * Tests that conversion from 32 bits to 8 bits arrays is correct.
       */
      function testDwordArrayToByteArray() {
        assertArrayEquals([0xDE, 0xAD, 0xBE, 0xEF],
                          e2e.dwordArrayToByteArray([0xDEADBEEF]));
        assertArrayEquals([0x00, 0x00, 0xCA, 0xFE],
                          e2e.dwordArrayToByteArray([0xCAFE]));
        assertArrayEquals([0x12, 0x34, 0x56, 0x78, 0x90, 0x00, 0x00, 0x00],
                          e2e.dwordArrayToByteArray(
                              [0x12345678, 0x90000000]));
      }


      /**
       * Tests that non-negative numbers are properly converted to big-endian byte arrays.
       */
      function testNumberToByteArray() {
        // Check that error is thrown when converting negative numbers.
        try {
          assertThrows(e2e.numberToByteArray(-1));
        }
        catch (e) {
          if (!(e instanceof e2e.error.InvalidArgumentsError)) {
            throw e;
          }
        }

        assertArrayEquals(e2e.numberToByteArray(0), [0x00]);
        assertArrayEquals(e2e.numberToByteArray(10000), [0x27, 0x10]);
        assertArrayEquals(e2e.numberToByteArray(16909060), [0x01, 0x02, 0x03, 0x04]);
      }

      /**
       * Tests that conversion from 8 bits array to 32 bits arrays is correct.
       */
      function testByteArrayToDwordArray() {
        assertArrayEquals([0xC0CAC01A],
                          e2e.byteArrayToDwordArray(
                              [0xC0, 0xCA, 0xC0, 0x1A]));
        assertArrayEquals([0x00001337],
                          e2e.byteArrayToDwordArray(
                              [0x00, 0x00, 0x13, 0x37]));
        assertArrayEquals([0xFFEEDDCC, 0xBB000000],
                          e2e.byteArrayToDwordArray(
                              [0xFF, 0xEE, 0xDD, 0xCC, 0xBB]));
      }

      function testLongToByteArray() {
        assertArrayEquals([0x12, 0x34, 0x56, 0x78, 0x9a, 0x00, 0x00, 0x00],
            e2e.longToByteArray(
                goog.math.Long.fromString("123456789a000000", 16)));
      }

      /**
       * Tests that conversion from a big-endian byte array to the corresponding number is correct.
       */
      function testByteArrayToNumber() {
        // Check that error is thrown for byte arrays exceeding 4 bytes.
        try {
          assertThrows(e2e.byteArrayToNumber([0x01, 0x01, 0x01, 0x01, 0x01]));
        }
        catch (e) {
          if (!(e instanceof e2e.error.InvalidArgumentsError)) {
            throw e;
          }
        }

        // Test conversion on a few examples.
        assertEquals(e2e.byteArrayToNumber([0x00, 0x00, 0x00, 0x00]), 0);
        assertEquals(e2e.byteArrayToNumber([0x01, 0x02, 0x03, 0x04]), 16909060);
      }


      // Tests from
      // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
      function testUTF8Conversion() {
        var samples = [
          {'inp': [0x0], 'out': '\u0000'},
          {'inp': [0xc2, 0x80], 'out': '\u0080'},
          {'inp': [0xe0, 0xa0, 0x80], 'out': '\u0800'},
          {'inp': [0xf0, 0x90, 0x80, 0x80], 'out': '\ud800\udc00'},
          {'inp': [0x7f], 'out': '\u007f'},
          {'inp': [0xdf, 0xbf], 'out': '\u07ff'},
          {'inp': [0xef, 0xbf, 0xbf], 'out': '\uffff'},
          {'inp': [0xf4, 0x8f, 0xbf, 0xbf], 'out': '\udbff\udfff'},
          {'inp': [0xed, 0x9f, 0xbf], 'out': '\ud7ff'},
          {'inp': [0xee, 0x80, 0x80], 'out': '\ue000'},
          {'inp': [0xf0, 0x9d, 0x8c, 0x86], 'out': '\ud834\udf06'}
        ];
        goog.array.forEach(
          samples,
          function(sample) {
            var str = e2e.byteArrayToString(sample.inp);
            assertEquals(sample.out, str);
            var enc = e2e.stringToByteArray(sample.out);
            assertArrayEquals(sample.inp, enc);
          });
      }


      // Tests from
      // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
      // Malformed sequences should be replaced as appropriate.
      function testMalformedUTF8Replacement() {
        var samples = [
          [0x80],
          [0xbf],
          [0x80, 0xbf],
          [0x80, 0xbf, 0x80],
          [0x80, 0xbf, 0x80, 0xbf],
          [0xc0, 0xaf],
          [0xe0, 0x80, 0xaf],
          [0xf0, 0x80, 0x80, 0xaf],
          [0xf8, 0x80, 0x80, 0x80, 0xaf],
          [0xfc, 0x80, 0x80, 0x80, 0x80, 0xaf]
        ];
        goog.array.forEach(
          samples,
          function(sample) {
            var str = e2e.byteArrayToString(sample);
            var len = str.length;
            assertEquals(len, sample.length);
            for (var i = 0; i < len; i++) {
              assertEquals(str.charCodeAt(i), 0xfffd);
            }
          });
      }


      // These javascript strings don't have a valid utf-8
      // encoding.
      function testInvalidUTF8Encoding() {
        var samples= [
          '\ud800',
          '\udbff',
          '\udc00',
          '\udfff',
          '\ud800\ud800'
        ];
        goog.array.forEach(
          samples,
          function(sample) {
            var mustThrow = assertThrows(function() {
              var enc = e2e.stringToByteArray(sample);
            });
            assertTrue(mustThrow instanceof e2e.error.InvalidArgumentsError);
          });
      }


      function testMojibakeNotTextDecoder() {
        asyncTest.waitForAsync('Waiting for byteArrayToString.');
        e2e.USE_TEXT_DECODER = false;
        e2e.byteArrayToStringAsync(
          [0xB0, 0xC2, 0xBC, 0xBC, 0xC6, 0xE0, 0xC8, 0xFE, 0xB7, 0xC3], 'euc-jp').addCallback(
          function(str) {
            asyncTest.continueTesting();
            // Japanese text, used to decode incorrectly.
            assertEquals('\u5B89\u5BA4\u5948\u7F8E\u6075', str);
          });
        e2e.USE_TEXT_DECODER = 'TextDecoder' in goog.global;
      }


      function testMojibakeTextDecoder() {
        asyncTest.waitForAsync('Waiting for byteArrayToString.');
        e2e.USE_TEXT_DECODER = 'TextDecoder' in goog.global;
        e2e.byteArrayToStringAsync(
          [0xB0, 0xC2, 0xBC, 0xBC, 0xC6, 0xE0, 0xC8, 0xFE, 0xB7, 0xC3], 'euc-jp').addCallback(
          function(str) {
            asyncTest.continueTesting();
            assertEquals('\u5B89\u5BA4\u5948\u7F8E\u6075', str);
          });
      }

      function testIsByte() {
        assert(e2e.isByte(0));
        assert(e2e.isByte(1));
        assert(e2e.isByte(255));
        for (var i = 0; i < 256; i++) {
          assert(e2e.isByte(i));
        }
        assertFalse(e2e.isByte(-1));
        assertFalse(e2e.isByte(undefined));
        assertFalse(e2e.isByte(1.1));
        assertFalse(e2e.isByte(-0.1));
        assertFalse(e2e.isByte(Infinity));
        assertFalse(e2e.isByte(-Infinity));
        assertFalse(e2e.isByte(NaN));
        assertFalse(e2e.isByte(null));
        assertFalse(e2e.isByte(undefined));
        assertFalse(e2e.isByte(true));
        assertFalse(e2e.isByte(false));
        assertFalse(e2e.isByte(new Number(1)));
        assertFalse(e2e.isByte(256));
        assertFalse(e2e.isByte((1<<31)*2));
      }

      function testIsUint32() {
        assert(e2e.isUint32(0));
        assert(e2e.isUint32(1));
        assert(e2e.isUint32(256));
        assert(e2e.isUint32(10000000));
        assert(e2e.isUint32(0xffffffff));
        assert(e2e.isUint32(0xabcdef01));
        assert(e2e.isUint32(0xf0f0f0f0));
        assertFalse(e2e.isUint32(-1));
        assertFalse(e2e.isUint32(undefined));
        assertFalse(e2e.isUint32(1.1));
        assertFalse(e2e.isUint32(-0.1));
        assertFalse(e2e.isUint32(Infinity));
        assertFalse(e2e.isUint32(-Infinity));
        assertFalse(e2e.isUint32(NaN));
        assertFalse(e2e.isUint32(null));
        assertFalse(e2e.isUint32(undefined));
        assertFalse(e2e.isUint32(true));
        assertFalse(e2e.isUint32(false));
        assertFalse(e2e.isUint32(new Number(1)));
        assertFalse(e2e.isUint32((1<<31)*2));
      }

      function testCompareByteArray() {
        assert(e2e.compareByteArray([1, 2, 3], [1, 2, 3]));
        assertFalse(e2e.compareByteArray([true, 2, 3], [1, 2, 3]));
        assertFalse(e2e.compareByteArray([0x100, 2, 3], [0x100, 2, 3]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [1, 2, 3, 0]));
        assertFalse(e2e.compareByteArray([1, 2, 3, 4], [1, 2, 3, undefined]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [1, 2, 3, undefined]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [0, 1, 2, 3]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [1, 2, 3, Infinity]));
        assertFalse(e2e.compareByteArray([0], [undefined]));
        assertFalse(e2e.compareByteArray([], [undefined]));
        assertFalse(e2e.compareByteArray([1], [2]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [2, 2, 3]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [1, 2, 1]));
        assertFalse(e2e.compareByteArray([1, 2, 3, 4, 5, 6], [1, 2, 3]));
        assertFalse(e2e.compareByteArray([1, 2, 3], [1, 2, 3, 4, 5, 6, 7, 8]));
      }

      function testIncrementByteArray() {
        var x = [];
        assertArrayEquals([], e2e.incrementByteArray(x));
        assertArrayEquals([], x);
        x = [0];
        assertArrayEquals([1], e2e.incrementByteArray(x));
        assertArrayEquals([1], x);
        x = [255];
        assertArrayEquals([0], e2e.incrementByteArray(x));
        assertArrayEquals([0], x);
        x = [0, 0, 0, 0];
        assertArrayEquals([0, 0, 0, 1], e2e.incrementByteArray(x));
        assertArrayEquals([0, 0, 0, 1], x);
        assertArrayEquals([0, 0, 0, 2], e2e.incrementByteArray(x));
        assertArrayEquals([0, 0, 0, 2], x);
        x = [1, 2, 3, 4];
        assertArrayEquals([1, 2, 3, 5], e2e.incrementByteArray(x));
        assertArrayEquals([1, 2, 3, 5], x);
        x = [1, 2, 3, 255];
        assertArrayEquals([1, 2, 4, 0], e2e.incrementByteArray(x));
        assertArrayEquals([1, 2, 4, 0], x);
        assertArrayEquals([1, 2, 4, 1], e2e.incrementByteArray(x));
        assertArrayEquals([1, 2, 4, 1], x);
        x = [7, 255, 255, 255];
        assertArrayEquals([8, 0, 0, 0], e2e.incrementByteArray(x));
        assertArrayEquals([8, 0, 0, 0], x);
        x = [255, 255, 255, 255];
        assertArrayEquals([0, 0, 0, 0], e2e.incrementByteArray(x));
        assertArrayEquals([0, 0, 0, 0], x);
      }

      function assertTypedArrayEquals(a, b, opt_c) {
        assertArrayEquals.apply(null, goog.array.map(arguments, function(e) {
          return e instanceof Uint8Array ? goog.array.clone(e) : e;
            }));
      };

      function testIncrementByteArrayWithUint8Array() {
        var x = new Uint8Array([1, 2, 3]);
        assertTypedArrayEquals([1, 2, 4], e2e.incrementByteArray(x));
        x = new Uint8Array([1, 2, 3, 4, 255]);
        assertTypedArrayEquals([1, 2, 3, 5, 0], e2e.incrementByteArray(x));
        x = new Uint8Array([9, 255, 255, 255]);
        assertTypedArrayEquals([10, 0, 0, 0], e2e.incrementByteArray(x));
      }


      function testImmutableArray() {

        // Check methods do the right thing with empty values.
        var safe = new e2e.ImmutableArray([]);
        assertEquals(0, safe.size());
        assertTrue(typeof(safe.get(0)) == 'undefined');
        e2e.ImmutableArray.forEach(safe,
          function(e) {
            fail('Should not be called.');
          });

        // Check we can initialize with arrays and safearrays.
        safe = new e2e.ImmutableArray([1, 2, 3]);
        assertEquals(3, safe.size());
        var safe2 = new e2e.ImmutableArray(safe);
        assertEquals(3, safe2.size());
        // Elements should be the same.
        e2e.ImmutableArray.forEach(safe,
          function(v, idx) {
            assertEquals(safe2.get(idx), v);
          });

        // check that push works as expected.
        var safe2 = e2e.ImmutableArray.pushCopy(safe, 4);
        assertEquals(3, safe.size());
        assertTrue(typeof(safe.get(3)) == 'undefined');
        assertEquals(3, safe.get(2));
        assertEquals(4, safe2.size());
        assertEquals(4, safe2.get(3));
        // Test behavior when the provided array is null.
        safe2 = e2e.ImmutableArray.pushCopy(null, 4);
        assertEquals(1, safe2.size());
        assertEquals(4, safe2.get(0));

        // Check concatenation works as expected.
        safe2 = e2e.ImmutableArray.concat(safe, new e2e.ImmutableArray([4, 5]));
        assertEquals(3, safe.size());
        assertEquals(5, safe2.size());
        for (var i = 0; i < 5; i++) {
          assertEquals(i + 1, safe2.get(i));
        }

        // Check forEach works as expected on the index.
        var checkidx = 0;
        e2e.ImmutableArray.forEach(safe2, function(v, idx) {
          assertEquals(checkidx, idx);
          assertEquals(idx + 1, v);
          checkidx++;
        });

        // Check splice works as expected.
        safe2 = e2e.ImmutableArray.spliceCopy(safe, 1, 1);
        assertEquals(3, safe.size());
        assertEquals(2, safe2.size());
        assertEquals(1, safe2.get(0));
        assertEquals(3, safe2.get(1));
        safe2 = e2e.ImmutableArray.spliceCopy(safe, 1, 1, 5);
        assertEquals(3, safe.size());
        assertEquals(3, safe2.size());
        assertEquals(1, safe2.get(0));
        assertEquals(5, safe2.get(1));
        assertEquals(3, safe2.get(2));

        // Check state is stored as expected.
        assertTrue(typeof(safe.getState()) == 'undefined');
        assertTrue((new e2e.ImmutableArray(safe, true)).getState());
        assertFalse((new e2e.ImmutableArray(safe, false)).getState());
        assertEquals(42, (new e2e.ImmutableArray(safe, 42)).getState());
      }

</script>
